package com.type.TypingDetector;

public class FourierTransform {
	
    private int originalDataLength = 0;     // original data length value; the working data length may be altered by deletion or padding
    private int fftDataLength = 0;          // working data length - usually the smallest power of two that is either equal to originalDataLength or larger than originalDataLength
	private boolean dataAltered = false;    // set to true if originalDataLength altered, e.g. by point deletion or padding.
	private double[] fftData = null;        // array to hold a data set of complex numbers arranged as alternating	                                        // real and imaginary parts, e.g. real_0 imag_0, real_1 imag_1, for the fast Fourier Transform method
	private double[] fftCorr = null;        // corresponding array to hold the  data to be correlated with first data set
	private double[] fftResp = null;        // corresponding array to hold the  response to be convolved with first data set
	private boolean fftDataSet = false;     // if true - the fftData array has been filled, if false - it has not.
	private double[] transformedDataFft = null; // transformed data set of double adjacent real and imaginary parts
	private double[] fftDataWindow = null;
	private double[] weights = null;        //  windowing weights
	private double sumOfSquaredWeights = 0.0D;
	private int segmentLength = 0;	        //	Number of of data points in a segment

	
	// constuctor entering a data array of real numbers
	public FourierTransform(double[] realData){
	    this.originalDataLength = realData.length;
	    this.fftDataLength = FourierTransform.nextPowerOfTwo(this.originalDataLength);

	    this.fftData = new double[2*this.fftDataLength];
	    int j = 0;
	    for(int i=0; i<this.fftDataLength; i++){
	        this.fftData[j] = realData[i];
	        j++;
	        this.fftData[j] = 0.0D;
	        j++;
	    }
	    this.fftDataSet = true;

	    this.fftDataWindow = new double[2*this.fftDataLength];
	    this.weights = new double[this.fftDataLength];
	    //this.sumOfSquaredWeights = windowData(this.fftData, this.fftDataWindow, this.weights);

	    this.transformedDataFft = new double[2*this.fftDataLength];
	    this.segmentLength = this.fftDataLength;

	}

	
	public void transform(){

        // set up data array
        int isign = 1;
        if(!this.fftDataSet)throw new IllegalArgumentException("No data has been entered for the Fast Fourier Transform");
	    if(this.originalDataLength!=this.fftDataLength){
	        System.out.println("Fast Fourier Transform data length ," + this.originalDataLength + ", is not an integer power of two");
	        System.out.println("WARNING!!! Data has been padded with zeros to fill to nearest integer power of two length " + this.fftDataLength);
	    }

        // Perform fft
        double[] hold = new double[this.fftDataLength*2];
        for(int i=0; i<this.fftDataLength*2; i++)hold[i] = this.fftDataWindow[i];
        basicFft(hold, this.fftDataLength, isign);
        for(int i=0; i<this.fftDataLength*2; i++)this.transformedDataFft[i] = hold[i];

     
    }

	// Base method for performing a Fast Fourier Transform
    // Based on the Numerical Recipes procedure four1
    // If isign is set to +1 this method replaces fftData[0 to 2*nn-1] by its discrete Fourier Transform
    // If isign is set to -1 this method replaces fftData[0 to 2*nn-1] by nn times its inverse discrete Fourier Transform
    // nn MUST be an integer power of 2.  This is not checked for in this method, fastFourierTransform(...), for speed.
    // If not checked for by the calling method, e.g. powerSpectrum(...) does, the method checkPowerOfTwo() may be used to check this.
    // The real and imaginary parts of the data are stored adjacently
    // i.e. fftData[0] holds the real part, fftData[1] holds the corresponding imaginary part of a data point
    // data array and data array length over 2 (nn) transferred as arguments
    // result NOT returned to this.transformedDataFft
    // Based on the Numerical Recipes procedure four1
    public void basicFft(double[] data, int nn, int isign)
    {
        double dtemp = 0.0D, wtemp = 0.0D, tempr = 0.0D, tempi = 0.0D;
        double theta = 0.0D, wr = 0.0D, wpr = 0.0D, wpi = 0.0D, wi = 0.0D;
	    int istep = 0, m = 0, mmax = 0;
	    int n = nn << 1;
	    int j = 1;
	    int jj = 0;
	    for (int i=1;i<n;i+=2) {
	        jj = j-1;
		    if (j > i) {
		        int ii = i-1;
		        dtemp = data[jj];
		        data[jj] = data[ii];
		        data[ii] = dtemp;
		        dtemp = data[jj+1];
		        data[jj+1] = data[ii+1];
		        data[ii+1] = dtemp;
		    }
		    m = n >> 1;
		    while (m >= 2 && j > m) {
			    j -= m;
			    m >>= 1;
		    }
		    j += m;
	    }
	    mmax=2;
	    while (n > mmax) {
		    istep=mmax << 1;
		    theta=isign*(6.28318530717959D/mmax);
		    wtemp=Math.sin(0.5D*theta);
		    wpr = -2.0D*wtemp*wtemp;
		    wpi=Math.sin(theta);
		    wr=1.0D;
		    wi=0.0D;
		    for (m=1;m<mmax;m+=2L) {
			    for (int i=m;i<=n;i+=istep) {
			        int ii =  i - 1;
					jj=ii+mmax;
				    tempr=wr*data[jj]-wi*data[jj+1];
				    tempi=wr*data[jj+1]+wi*data[jj];
				    data[jj]=data[ii]-tempr;
				    data[jj+1]=data[ii+1]-tempi;
				    data[ii] += tempr;
				    data[ii+1] += tempi;
			    }
			    wr=(wtemp=wr)*wpr-wi*wpi+wr;
			    wi=wi*wpr+wtemp*wpi+wi;
		    }
		    mmax=istep;
	    }
    }

 // Get the transformed data array as adjacent real and imaginary pairs
    public double[] getTransformedDataAsAlternate(){
		return this.transformedDataFft;
	}
    
 // returns nearest power of two that is equal to or lower than argument length
    public static int lastPowerOfTwo(int len){

	    boolean test0 = true;
	    while(test0){
	        if(FourierTransform.checkPowerOfTwo(len)){
	            test0 = false;
	        }
	        else{
	            len--;
	        }
	    }
	    return len;
    }

    // returns nearest power of two that is equal to or higher than argument length
	public static int nextPowerOfTwo(int len){

	    boolean test0 = true;
	    while(test0){
	        if(FourierTransform.checkPowerOfTwo(len)){
	            test0 = false;
	        }
	        else{
	            len++;
	        }
	    }
	    return len;
	}

	// Checks whether the argument n is a power of 2
	public static boolean checkPowerOfTwo(int n){
		boolean test = true;
		int m = n;
		while(test && m>1){
			if((m % 2)!=0){
				test = false;
			}
			else{
				m /= 2;
			}
		}
		return test;
	}

	public void setData(double[] realData){
        this.originalDataLength = realData.length;
	    this.fftDataLength = FourierTransform.nextPowerOfTwo(this.originalDataLength);

	    this.fftData = new double[2*this.fftDataLength];
	    int j = 0;
	    for(int i=0; i<this.fftDataLength; i++){
	        this.fftData[j] = realData[i];
	        j++;
	        this.fftData[j] = 0.0D;
	        j++;
	    }
	    this.fftDataSet = true;

	    this.fftDataWindow = new double[2*this.fftDataLength];
	    this.weights = new double[this.fftDataLength];
	    //this.sumOfSquaredWeights = windowData(this.fftData, this.fftDataWindow, this.weights);

	    this.transformedDataFft = new double[2*this.fftDataLength];
	}

}
